package com.irdstudio.efp.esb.service.client.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import com.irdstudio.efp.esb.common.constant.ElectronicSignatureConstant;
import com.irdstudio.efp.esb.common.util.JsonLogUtil;
import com.irdstudio.efp.esb.service.client.ESBBeanCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PascalNameFilter;
import com.irdstudio.efp.esb.common.client.req.EsbReqServiceBean;
import com.irdstudio.efp.esb.common.client.resp.EsbRespRetInfBean;
import com.irdstudio.efp.esb.common.client.resp.EsbRespServiceBean;
import com.irdstudio.efp.esb.common.client.resp.EsbRespSysHeadBean;
import com.irdstudio.efp.esb.service.client.ESBClientFactory;
import com.irdstudio.efp.esb.service.client.ESBConfig;
import com.irdstudio.efp.esb.service.client.HttpClientFactory;
import com.irdstudio.basic.framework.core.exception.ESBException;

/**
 * @description:ESB客户端
 * @date:2018年12月18日 上午10:18:28
 * @author:hengyh
 */
public class ESBClientImpl implements ESBClient {
	// FIXME 这里可以实现的更优雅，这里前三个参数与后三个参数的性质不同，前面三个是请求域（scope=request）的，
	// 后面三个是全局的，如果你看到了这段话，请将前三个参数封装到一个ESBRequest的对象中
	// by: hengyh
	/** ESB请求报文体 **/
	private final Object sendBodyBean;
	/** 接口交易代码 **/
	private final String tradeNo;
	/** 服务场景 **/
	private final String scene;
	/** RestTemplate **/
	private final HttpClientFactory httpClientFactory;
	/** ESB配置，提供url等配置信息 **/
	private final ESBConfig config;
	/** ESB请求Bean的创建器 **/
	private final ESBBeanCreator creator;

	private static final Logger log = LoggerFactory.getLogger(ESBClientImpl.class);
	
	public ESBClientImpl(ESBClientFactory.Builder builder) {
		this.sendBodyBean = Objects.requireNonNull(builder.getToSend());
		this.tradeNo = Objects.requireNonNull(builder.getTradeNo());
		this.scene = Objects.requireNonNull(builder.getScene());
		this.httpClientFactory = Objects.requireNonNull(builder.getFactory());
		this.config = Objects.requireNonNull(builder.getConfig());
		this.creator = Objects.requireNonNull(builder.getESBBeanFactory());
	}
	

	@Override
	public EsbRespServiceBean sendAndReceive() throws ESBException {
		try {
			EsbReqServiceBean clientBean = creator.create(tradeNo, scene, sendBodyBean);
			String recvJson = doSend(clientBean, config.getUrl());
			String logJson = new String(recvJson);
			logJson = JsonLogUtil.changeJSONStrWithBody(logJson, ElectronicSignatureConstant.KEY_CONTR_FL_STRM);
			log.info("接收到Json：" + logJson);
			EsbRespServiceBean recvBean = JSONObject.parseObject(recvJson, EsbRespServiceBean.class);
			List<EsbRespRetInfBean> array = Optional.ofNullable(recvBean.getSysHead())
					.map(EsbRespSysHeadBean::getRetInfArry)
					.orElseGet(ArrayList::new);
			if ( array
				.stream()
				// 有些接口在系统头的返回数组中没有RetCd字段，过滤掉为空的  by zhoucs1 时间：2019/06/20
				.map(EsbRespRetInfBean::getRetCd).filter(Objects::nonNull)  
				.anyMatch(i -> i.startsWith("ESB"))
			) { 
				String errorMsg = "ESB接口异常, 异常信息数组：" + array;
				throw new ESBException(errorMsg);
			}
			return recvBean;
		} catch (Exception e) {
			log.error(e.getMessage());
			throw new ESBException(e);
		} 
	}
	
	/**----------------------------------------------------**/
	/**                   private methods                  **/
	/**----------------------------------------------------**/
	
	private String doSend(EsbReqServiceBean clientBean, String url) {
		// 将发送的bean转换为json
		String sendJson = JSONObject.toJSONString(clientBean,new PascalNameFilter());
		String logJson = JSONObject.toJSONString(clientBean,new PascalNameFilter());
		logJson = JsonLogUtil.changeJSONStrWithBody(logJson,ElectronicSignatureConstant.KEY_CONTR_FL_STRM);
		log.info("发送Json: " + logJson + ",ESB目标url:" + url);
		HttpHeaders headers = new HttpHeaders();
		headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		HttpEntity<String> entity = new HttpEntity<String>(sendJson, headers);
		ResponseEntity<String> recvEntity = 
				httpClientFactory.getClient()
				.exchange(url, HttpMethod.POST, entity, String.class, new Object[]{});
		return recvEntity.getBody();
	}
}
